<template>
  <div class="scrm-scroller">
    <slot name="fixerContainer"></slot>
    <div class="scrm-scroller-container"
    :style="[
      {
        paddingTop: paddingTop + 'px'
      }
    ]">
      <div class="scrm-scroll-box"
      id="scrollCon"
      ref="scrollCon"
      :style="[
        {
          height: containerHeight + 'px',
          width: windowWidth + 'px'
        }
      ]"
      >
        <div class="scroll-content"
        ref="scrollContent"
        :style="[
          {
            paddingTop: topOffset + 'px',
            paddingBottom: paddingBottom + 'px',
            minHeight: contentHeight + 'px',
          },
          contentStyle
        ]">
          <slot v-if="this.$listeners.refresh && more" name="topSpinner">
            <div id="topSpinner" class="spinner-holder" ref="topSpinner">
              <spinner></spinner>
            </div>
          </slot>
          <div class="scroll-body" ref="scrollBody"><slot></slot></div>
          <slot v-if="this.$listeners.load && more" name="bottomSpinner">
            <spinner></spinner>
          </slot>
        </div>
      </div>
    </div>
    <slot name="footer"></slot>
  </div>
</template>
<script>
var zepto = require('npm-zepto')
var Velocity = require('velocity-animate')
import spinner from '../components/spinner'
import Q from 'q'

export default {
  props: {
    containerStyle: {
      type: Object
    },
    contentStyle: {
      type: Object
    },
    topRefs: {
      type: Array
    },
    bottomRefs: {
      type: Array
    },
    fixer: {
      type: HTMLDivElement | Object
    },
    more: {
      type: Boolean
    },
    loading: {
      type: Boolean
    },
    topOffset: {
      type: Number,
      default: 0
    },
    threshold: {
      type: Number,
      default: 30
    },
    scrollToBottom: {
      type: Boolean
    },
    isHideFixer: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      // Classes assigned to nav on scroll
      gutter: 40,
      id: '',
      lock: false,
      paddingTop:0,
      collapse: false,
      scrollContainer: '',
      defaultTop: 0,
      scrollContent: '',
      contentOverHeight: 150,
      containerHeight: 0,
      contentHeight: 0,
      paddingBottom: 0,
      windowHeight: '',
      windowWidth: '',
      scrollState: 0, // Used to keep track of scroll position
    }
  },
  watch: {
    bottomRefs () {
      this.calculateSize()
    },
    topRefs () {
      this.calculateSize()
    },
    loading (value) {
      if (!this.target && value=== false && this.scrollTop() < this.getTopOffset()) {
        this.scrollToTop()
      } else if(this.target === this.scrollTop()) {
        this.target = ''
      }
    },
    more (value) {
      // if (value === true) this.scrollToTop()
    }
  },
  components: {
    spinner
  },
  methods: {
    scrollDetect(home, down, up, end) {
      // Current scroll position
      const currentScroll = this.scrollTop();
      // console.log(this.scrollTop(), 'console.log(this.scrollTop())')
      if (this.scrollTop() <= this.gutter) {
        home();
      } else if (currentScroll > (this.scrollState)) {
        if (this.isReachBottom()) {
          console.log('isReachBottom')
          return end()
        } else {
          down();
        }
      } else {
        // console.log('up')
        up();
      }
      // Set previous scroll position
      this.scrollState = this.scrollTop();
    },
    isReachBottom () {
      const o = this.$refs.scrollCon
      return o.offsetHeight + o.scrollTop == o.scrollHeight
    },
    // Returns current scroll position
    scrollTop() {
      return this.scrollContainer.scrollTop;
    },
    // Called when scroll is in initial position
    scrollHome() {
      // console.log('scrollHome')
      if (!this.isHideFixer) {
        this.hideFixer().then(this.refresh)
      } else {
        this.refresh()
      }

      // this.scrollToTop()
    },
    showFixer () {
      return Q.Promise((resolve, reject) => {
        if (this.fixer && !this.showEnded) {
          this.showEnded = true
          this.fixerShowVelocity = Velocity(this.fixer,
            {
              opacity: 1,
              marginTop: '0px'
            },
            {
              duration: 200,         // 动画执行时间
              easing: "linear"
          }).then(() => {
            this.calculateSize()
            this.showEnded = false
            this.collapse = false
            this.$emit('showFixer')
            resolve()
          })
        }
      })
    },
    refresh () {
      if (this.more && this.scrollTop() <= this.threshold && !this.lock) {
        this.$emit('refresh')
      }
    },
    enableEvents () {
      this.lock = false
    },
    disableEvents () {
      this.lock = true
    },
    hideFixer () {

      return Q.Promise((resolve, reject) => {
        if (this.fixer && !this.hideEnded) {
          this.hideEnded = true
          let height = this.fixer.offsetHeight
          Velocity(this.fixer, 'stop')

          this.fixerHideVelocity = Velocity(this.fixer, {
              opacity: 0,
              marginTop: -height + 'px'
            },
            {
              duration: 200,         // 动画执行时间
              easing: "linear"
            }
          ).then(() => {
            this.calculateSize()
            this.hideEnded = false
            this.collapse = true
            this.$emit('hideFixer')
            resolve()
          })
        } else {
          resolve()
        }
      })
    },
    scrollEnd () {
      // console.log('scrollEnd')
      if (!this.lock) {
        this.isHideFixer || this.hideFixer()
        if (this.more) {
          this.$emit('load')
        }
      }
    },
    // Called when scrolled down
    scrollDown() {
      // this.navBar.collapse = true;
      // this.navBar.open = false;
      // console.log('scrollDown')
      if (!this.lock) {
        this.isHideFixer || this.hideFixer()
        this.$emit('down')
      }
    },
    // Called when scolled up
    scrollUp() {
      // this.navBar.collapse = false;
      // this.navBar.open = true;
      this.$emit('up')
    },
    calculateSize () {
      this.paddingBottom = this.getPaddingBottom()
      this.paddingTop = this.getPaddingTop()

      this.containerHeight = this.getContainerHeight()
      this.contentHeight = this.getContentHeight()
      // console.log('this.contentHeight', this.contentHeight)
      this.windowHeight = window.innerHeight
      this.windowWidth = window.innerWidth
    },
    getTopOffset () {
      let scrollTop
      scrollTop = this.$refs.scrollBody.offsetTop - this.scrollContainer.offsetTop + this.topOffset
      return scrollTop || this.defaultTop
    },
    scrollToTop () {
      // console.log('scrollToTop')
      this.disableEvents()
      this.scrollContainer.scrollTop = this.getTopOffset()
      this.target = this.getTopOffset()
    },
    scrollToEnd () {

      this.disableEvents()
      const contentHeight  = this.scrollContent.offsetHeight
      const scrollTop = contentHeight - this.containerHeight
      this.target = scrollTop
      // console.log('scrollToEnd', scrollTop)
      this.scrollContainer.scrollTop = scrollTop
    },
    getContainerHeight () {
      return window.innerHeight - this.paddingTop - this.paddingBottom
    },
    getContentHeight () {
      return this.containerHeight + this.paddingBottom + this.contentOverHeight
    },
    getRrefOffsetHeight (ref) {
      let offsetHeight = 0
      if (ref) {
        if (ref.hasOwnProperty('$el')) {
          offsetHeight = ref.$el.offsetHeight
        } else {
          offsetHeight = ref.offsetHeight
        }
      }
      return offsetHeight
    },
    getPaddingTop () {
      let paddingTop = 0
      if (this.topRefs && typeof this.topRefs === 'object') {
        this.topRefs.forEach((elem, index) =>
          paddingTop += this.getRrefOffsetHeight(elem))
      }
      // console.log('paddingTop', paddingTop)
      return paddingTop
    },
    getPaddingBottom () {
      let ref
      let paddingBottom = 0
      if (this.bottomRefs && typeof this.bottomRefs === 'object') {
        this.bottomRefs.forEach((elem, index) =>
          paddingBottom += this.getRrefOffsetHeight(elem))
      }
      // console.log('paddingBottom', paddingBottom)
      return paddingBottom
    },
    excuteAnimation () {
      if (this.fixerHideVelocity || this.fixerShowVelocity) {
        Velocity(this.fixer, 'stop')
      }
      this.scrollDetect(this.scrollHome, this.scrollDown, this.scrollUp, this.scrollEnd);
      this.fixerHideVelocity = null
      this.fixerShowVelocity = null
    },
    releaseLockWhenReachTarget () {
      if (this.target && this.lock
        && this.scrollContainer.scrollTop === this.target) {
          setTimeout(() => {
            this.lock = false
            this.target = ''
          })
      }
    },
    scrollHanler (e) {
      if(this.scrollId) cancelAnimationFrame(this.scrollId)
      this.scrollId = requestAnimationFrame(() => {
        this.excuteAnimation()
        cancelAnimationFrame(this.scrollId)
      })
      this.releaseLockWhenReachTarget()
    },
    initScroll () {
      zepto(this.scrollContainer).on('scroll', this.scrollHanler)
    },
    unbindScroll () {
      zepto(this.scrollContainer).off('scroll')
    }
  },
  mounted() {
    this.id = Math.floor(Math.random()*100000)
    this.calculateSize()
    this.scrollContainer = this.$refs.scrollCon
    this.scrollContent = this.$refs.scrollContent
    this.scrollToTop()
    this.$nextTick(() => {
      this.initScroll()
    })
    zepto(window).on('resize.scro_con' + this.id, () => {
      this.calculateSize()
    })
  },
  destroyed () {
    clearTimeout(this.ttid)
    zepto(window).off('resize.scro_con' + this.id)
  }
}
</script>
<style lang="less">
.scrm-scroller {
  position: relative;
  z-index: 1;

  .scrm-scroll-box {
    height: 100vh;
    width: 100%;
    overflow-x: hidden;
    overflow-y: scroll;
    -webkit-overflow-scrolling: touch;
    scroll-behavior: smooth;
  }

  .scroll-content {
    overflow:hidden;
  }

  .spinner-holder {
    width: 30px;
    height: 30px;
    margin: 20px auto 0;
  }

}

</style>
